﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Kokomo.Mirror.CliMetadata.Signatures
{
	public class Param : TypedElement
	{
		internal static readonly Param Int32Param = new Param() { type = PrimitiveTypeSpec.Get(SignatureElementType.Int32) };

		public CustomModifiers customModifiers;
		public bool isTypedByRef;
		public bool isByRef;
		public TypeSpec type;

		public override Type ResolveType(
			Module module,
			bool throwOnError,
			Type[] genericTypeArgs,
			Type[] genericMethodArgs
			)
		{
			if (this.isTypedByRef) return typeof(TypedReference);

			Type type = (this.type == null)
				? typeof(void)
				: this.type.ResolveType(module, throwOnError, genericTypeArgs, genericMethodArgs)
				;

			if ((object)type != null)
			{
				if (this.isByRef) type = type.MakeByRefType();
			}

			return type;
		}

		public bool Matches(
			ParameterInfo parameter,
			Module module,
			Type[] genericTypeArguments,
			Type[] genericMethodArguments
			)
		{
			if (parameter == null) throw new ArgumentNullException("parameter");

			Type parameterType = parameter.ParameterType;

			if (this.isByRef)
			{
				if (parameterType.IsByRef)
					parameterType = parameterType.GetElementType();
				else
					return false;
			}

			CliParameterInfo cliParamInfo = parameter as CliParameterInfo;
			bool signatureMatches = false;
			if (
				(cliParamInfo != null)
				&& (cliParamInfo.signature != null)
				)
			{
				signatureMatches = this.Equals(cliParamInfo.signature);
			}

			if (!signatureMatches)
			{
				if (!this.type.Matches(parameterType, module, genericTypeArguments, genericMethodArguments))
					return false;
			}

			Type[] requiredMods = parameter.GetRequiredCustomModifiers();
			Type[] optionalMods = parameter.GetOptionalCustomModifiers();

			return true;
		}
		public bool Matches(
			Module ownerModule,
			Param other,
			Module otherModule
			)
		{
			if ((object)ownerModule == null) throw new ArgumentNullException("ownerModule");
			if (other == null) throw new ArgumentNullException("other");
			if ((object)otherModule == null) throw new ArgumentNullException("otherModule");

			return (this.isTypedByRef)
				? other.isTypedByRef
				: (
					(this.isByRef == other.isByRef)
					&& (this.type.Matches(ownerModule, other.type, otherModule))
					);
		}

		public override string ToString()
		{
			return this.type.ToString();
		}

		public override bool Equals(object obj)
		{
			Param other = obj as Param;
			if (other == null) return false;

			bool equals =
				(object.Equals(this.customModifiers, other.customModifiers))
				&& (this.isTypedByRef == other.isTypedByRef)
				&& (this.isByRef == other.isByRef)
				&& (object.Equals(this.type, other.type))
				;
			return equals;
		}
	}
}
